package com.dlc.shop.live.common.service.impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.dlc.shop.bean.bo.ProductBO;
import com.dlc.shop.bean.dto.wukong.ChannelDTO;
import com.dlc.shop.bean.dto.wukong.RegisterOrLoginDTO;
import com.dlc.shop.bean.enums.LiveRoomStatusType;
import com.dlc.shop.bean.enums.ProdStatusEnums;
import com.dlc.shop.bean.model.ImChannel;
import com.dlc.shop.bean.param.LiveRoomParam;
import com.dlc.shop.bean.param.ProductParam;
import com.dlc.shop.bean.vo.wukong.RegisterOrLoginVO;
import com.dlc.shop.common.config.Constant;
import com.dlc.shop.common.constants.CacheNames;
import com.dlc.shop.common.exception.YamiShopBindException;
import com.dlc.shop.common.util.Json;
import com.dlc.shop.common.util.PageAdapter;
import com.dlc.shop.common.util.PageParam;
import com.dlc.shop.common.util.RedisUtil;
import com.dlc.shop.common.wukongim.constant.WuKongConstant;
import com.dlc.shop.dao.ProductMapper;
import com.dlc.shop.dao.UserMapper;
import com.dlc.shop.live.common.constant.LiveCacheNames;
import com.dlc.shop.live.common.dao.LiveRoomMapper;
import com.dlc.shop.live.common.dto.LiveRoomDto;
import com.dlc.shop.live.common.model.LiveRoom;
import com.dlc.shop.live.common.service.LiveRoomProdService;
import com.dlc.shop.live.common.service.LiveRoomService;
import com.dlc.shop.live.common.vo.LiveMsgItem;
import com.dlc.shop.live.common.vo.LiveRoomVO;
import com.dlc.shop.service.ImChannelService;
import com.dlc.shop.wukongim.service.WuKongImService;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Objects;

/**
 * @author lhd
 * @date 2020-08-05 08:53:17
 */
@Service
@AllArgsConstructor
@Slf4j
public class LiveRoomServiceImpl extends ServiceImpl<LiveRoomMapper, LiveRoom> implements LiveRoomService {

    private final LiveRoomMapper liveRoomMapper;

    private final UserMapper userMapper;

    private final LiveRoomProdService liveRoomProdService;

    private final ProductMapper productMapper;

    private final WuKongImService wuKongImService;

    private final ImChannelService imChannelService;

    private static final Integer SEARCH_PROD_NAME = 2;

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void saveLiveRoom(LiveRoom liveRoom) {
        // 检查直播间信息，设置主播用户id
        checkLiveRoomAndSetUserId(liveRoom, true);
        liveRoomMapper.insert(liveRoom);
        // 保存商品
        liveRoomProdService.save(liveRoom);
        // 创建频道
        String channelId = WuKongConstant.LIVE_CHANNEL_PREFIX + liveRoom.getRoomId();
        ChannelDTO channelDTO = new ChannelDTO(channelId, WuKongConstant.GROUP_CHAT, 1, null);
        wuKongImService.createChannel(channelDTO);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    @CacheEvict(cacheNames = LiveCacheNames.LIVE_ROOM_INFO, key = "#liveRoom.roomId")
    public void updateLiveRoom(LiveRoom liveRoom) {
        checkLiveRoomAndSetUserId(liveRoom, false);
        liveRoomMapper.updateById(liveRoom);
        // 更新商品
        liveRoomProdService.update(liveRoom);
    }

    private void checkLiveRoomAndSetUserId(LiveRoom liveRoom, boolean isSave) {
        Date startTime = liveRoom.getStartTime();
        Date endTime = liveRoom.getEndTime();
        if (isSave) {
            // 校验时间
            if (DateUtil.compare(new Date(), startTime) > 0) {
                // 开始时间不能少于当前时间
                throw new YamiShopBindException("yami.live.time.one.check");
            }
            if (DateUtil.compare(startTime, endTime) > 0) {
                // 开始时间不能大于结束时间
                throw new YamiShopBindException("yami.live.time.check");
            }
            boolean expiredEndTime = DateUtil.isIn(endTime, startTime, DateUtil.offsetMinute(startTime, 30)) ||
                    !DateUtil.isIn(endTime, startTime, DateUtil.offsetHour(startTime, 24));
            if (expiredEndTime) {
                // 开始和结束时间间隔必须大于30分钟，不得超过24小时
                throw new YamiShopBindException("yami.live.time.two.check");
            }
        }
        // 校验主播信息
        if (StrUtil.isBlank(liveRoom.getNickName()) || StrUtil.isBlank(liveRoom.getUserMobile())) {
            // 主播信息不能为空
            throw new YamiShopBindException("yami.live.anchor.check");
        }
        String userId = userMapper.getUserByUserMobileAndNickName(liveRoom.getUserMobile(), liveRoom.getNickName());
        if (Objects.isNull(userId)) {
            // 当前选择的主播不存在或被禁用，请检查主播信息是否正确
            throw new YamiShopBindException("yami.live.anchor.status.check");
        }
        liveRoom.setUserId(userId);
        // 商家不能在一个时间段创建两个直播间
        int countByShop = liveRoomMapper.countRoomByParam(liveRoom.getShopId(), null, liveRoom.getRoomId(), startTime, endTime);
        if (countByShop > 0) {
            // 该时间段下已设有直播间，无法再配置新的直播间
            throw new YamiShopBindException("yami.live.shop.already.exist");
        }
        // 一个主播账号不能同时开播两场直播
        int countByUser = liveRoomMapper.countRoomByParam(null, userId, liveRoom.getRoomId(), startTime, endTime);
        if (countByUser > 0) {
            // 主播在该时间段下已设有直播间，无法再配置新的直播间
            throw new YamiShopBindException("yami.live.user.already.exist");
        }
    }

    @Override
    public List<LiveRoomParam> getLivingRoomByProdId(Long prodId) {
        return liveRoomMapper.getLivingRoomByProdId(prodId);
    }

    @Override
    public IPage<LiveRoomDto> pageRoomAndDetail(PageParam<LiveRoomDto> page, LiveRoomParam liveRoom) {
        if (Objects.equals(liveRoom.getSearchType(), SEARCH_PROD_NAME) && StrUtil.isNotBlank(liveRoom.getProductName())) {
            // 搜索商品名
            ProductParam productParam = new ProductParam();
            productParam.setProdName(liveRoom.getProductName());
            productParam.setStatus(ProdStatusEnums.NORMAL.getValue());
            List<ProductBO> productBOList = productMapper.listSimple(productParam);
            List<Long> prodIds = productBOList.stream().map(ProductBO::getProdId).toList();
            if (CollUtil.isEmpty(prodIds)) {
                page.setRecords(new ArrayList<>());
                page.setTotal(0L);
                return page;
            }
            liveRoom.setProdIds(prodIds);
        }
        List<LiveRoomDto> list = liveRoomMapper.pageRoomAndDetail(new PageAdapter(page), liveRoom);
        page.setRecords(list);
        page.setTotal(liveRoomMapper.countRoomAndDetail(new PageAdapter(page), liveRoom));
        return page;
    }

    @Override
    public IPage<LiveRoom> pageLiveRoom(PageParam<LiveRoom> page, LiveRoom liveRoom) {
        IPage<LiveRoom> liveRoomPage = liveRoomMapper.pageLiveRoom(page, liveRoom);
        for (LiveRoom record : liveRoomPage.getRecords()) {
            if (Objects.isNull(record.getNickName())) {
                record.setNickName("*用户已注销*");
                record.setUserMobile("*用户已注销*");
            }
        }
        return liveRoomPage;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    @CacheEvict(cacheNames = LiveCacheNames.LIVE_ROOM_INFO, key = "#roomId")
    public Boolean removeRoomById(Long roomId, Long shopId) {
        LiveRoom liveRoom = getById(roomId);
        if (Objects.isNull(liveRoom)) {
            // 直播间不存在，请刷新后重试
            throw new YamiShopBindException("yami.live.not.exist");
        }
        if (!Objects.equals(shopId, liveRoom.getShopId()) && !Objects.equals(Constant.PLATFORM_SHOP_ID, shopId)) {
            // 没有权限进行操作！
            throw new YamiShopBindException("yami.no.auth");
        }
        if (Objects.equals(liveRoom.getLiveStatus(), LiveRoomStatusType.LIVING.value())) {
            // 直播已开始，无法删除直播间
            throw new YamiShopBindException("yami.live.status.delete.error");
        }
        boolean result = removeById(roomId);
        // 删除直播间关联商品
        liveRoomProdService.delete(roomId);
        return result;
    }

    @Override
    @CacheEvict(cacheNames = LiveCacheNames.LIVE_ROOM_INFO, key = "#liveRoom.roomId")
    @Transactional(rollbackFor = Exception.class)
    public void offline(LiveRoom liveRoom) {
        LiveRoom dbLiveRoom = getById(liveRoom.getRoomId());
        if (Objects.isNull(dbLiveRoom)) {
            // 直播间不存在，请刷新后重试
            throw new YamiShopBindException("yami.live.not.exist");
        }
        if (Objects.equals(dbLiveRoom.getLiveStatus(), LiveRoomStatusType.OFFLINE.value())) {
            // 直播间已违规下架，无法修改信息
            throw new YamiShopBindException("yami.live.status.upate.offline");
        }
        update(new LambdaUpdateWrapper<LiveRoom>()
                .eq(LiveRoom::getRoomId, liveRoom.getRoomId())
                .set(LiveRoom::getLiveStatus, LiveRoomStatusType.OFFLINE.value())
                .set(LiveRoom::getRemark, liveRoom.getRemark()));
        // 发送直播结束消息
        LiveMsgItem statusMsg = new LiveMsgItem(WuKongConstant.LIVE_STATUS, LiveRoomStatusType.FINISHED.value());
        wuKongImService.sendLiveRoomMessage(WuKongConstant.LIVE_CHANNEL_PREFIX + liveRoom.getRoomId(), Json.toJsonString(statusMsg), null);
    }

    @Override
    @Cacheable(cacheNames = LiveCacheNames.LIVE_ROOM_INFO, key = "#roomId")
    public LiveRoomVO getLivingRoomInfo(String userId, Long roomId) {
        return liveRoomMapper.getLiveRoomInfo(userId, roomId);
    }

    @Override
    public LiveRoomVO getByUserId(String userId) {
        return liveRoomMapper.getLiveRoomInfo(userId, null);
    }

    @Override
    public void updateLiveRoomStatus(Integer liveStatus, List<Long> roomIds) {
        liveRoomMapper.updateLiveRoomStatus(liveStatus, roomIds);
        delCache(roomIds);
    }

    @Override
    public Integer getLiveUserCount(Long roomId, Integer changeCount) {
        // 缓存中获取观看人数
        Integer count = RedisUtil.get(LiveCacheNames.LIVE_CHANNEL_PERSON_COUNT + roomId);
        if (Objects.isNull(count)) {
            count = changeCount > 0 ? 1 : 0;
            // // 缓存一个观看人数，时限24小时，因为直播最多24小时结束
            RedisUtil.set(LiveCacheNames.LIVE_CHANNEL_PERSON_COUNT + roomId, count, 86400);
        } else {
            if (changeCount > 0) {
                RedisUtil.incr(LiveCacheNames.LIVE_CHANNEL_PERSON_COUNT + roomId, changeCount);
            } else if (changeCount < 0 && count + changeCount >= 0) {
                RedisUtil.decr(LiveCacheNames.LIVE_CHANNEL_PERSON_COUNT + roomId, -changeCount);
            }
            count += changeCount;
        }
        return count;
    }

    @Override
    public RegisterOrLoginVO liveUserLogin(LiveRoomVO livingRoom, Long roomId, String token, String userId) {
        if (Objects.isNull(livingRoom)) {
            throw new YamiShopBindException("yami.live.room.exception.roomNotExist");
        }
        if (Objects.equals(livingRoom.getLiveStatus(), LiveRoomStatusType.FINISHED.value())) {
            throw new YamiShopBindException("yami.live.room.exception.liveFinish");
        }
        String uid = WuKongConstant.getLiveUserUid(userId, roomId);
        RegisterOrLoginDTO registerOrLoginDTO = new RegisterOrLoginDTO(uid, token, 0);
        // 注册
        wuKongImService.registerOrLogin(registerOrLoginDTO);
        String channelId = WuKongConstant.LIVE_CHANNEL_PREFIX + roomId;
        // 添加订阅者
        wuKongImService.addSubscriber(channelId, uid);
        RegisterOrLoginVO registerOrLoginVO = new RegisterOrLoginVO(uid, token);
        return registerOrLoginVO;
    }

    @Override
    public void quitUser(String userId, Boolean isDestroy) {
        // 客服
        wuKongImService.deviceQuit(new RegisterOrLoginDTO(WuKongConstant.USER + userId, -1));
        // 直播间
        // 主播
        List<Long> anchorRoomIds = liveRoomMapper.listLiveRoom(userId);
        if (CollUtil.isNotEmpty(anchorRoomIds)) {
            for (Long roomId : anchorRoomIds) {
                wuKongImService.deviceQuit(new RegisterOrLoginDTO(WuKongConstant.getLiveAnchorUid(userId, roomId), -1));
            }
        }
        // 直播用户
        List<Long> userRoomIds = liveRoomMapper.listLiveRoom(null);
        if (CollUtil.isNotEmpty(userRoomIds)) {
            for (Long userRoomId : userRoomIds) {
                wuKongImService.deviceQuit(new RegisterOrLoginDTO(WuKongConstant.getLiveUserUid(userId, userRoomId), -1));
            }
        }
        if (!isDestroy) {
            return;
        }
        List<ImChannel> imChannels = imChannelService.listChannelByUserId(userId);
        if (CollUtil.isNotEmpty(imChannels)) {
            List<String> uids = new ArrayList<>(imChannels.size());
            for (ImChannel imChannel : imChannels) {
                if (Objects.equals(imChannel.getShopId(), Constant.PLATFORM_SHOP_ID)) {
                    uids.add(WuKongConstant.getSysUid(imChannel.getEmployeeId()));
                } else {
                    uids.add(WuKongConstant.getShopUid(imChannel.getEmployeeId(), imChannel.getShopId()));
                }
            }
            wuKongImService.userMessageSendbatch(userId, uids, imChannels);
        }
    }

    private static void delCache(List<Long> roomIds) {
        List<String> keys = new ArrayList<>();
        for (Long roomId : roomIds) {
            keys.add(LiveCacheNames.LIVE_ROOM_INFO + CacheNames.UNION + roomId);
        }
        RedisUtil.del(keys);
    }
}
